<?php
/**
 * Created by PhpStorm.
 * User: s.utkin
 * Date: 13.08.14
 * Time: 11:08
 */

namespace Messenger;

use Messenger\Messages;
use Pinenut\Model2\Embedded\ReferenceContainer\Many;
use Users\Profile;
use Pinenut\Model2\Finder;

/**
 * Группа отношений. Содержит отношения пользователей к данной группе.
 *
 * @package Profiles
 */
class Groups extends \Model
{
	/**
	 * Отношения пользователей к группе
	 *
	 * @var \Pinenut\Model2\Embedded\ReferenceContainer\Many
	 */
	public $relations;

	/**
	 * Количество непросмотренных сообщений от пользователя
	 *
	 * @var \Pinenut\Model2\Embedded\ReferenceContainer\Many
	 */
	public $unseenMessages;

	/**
	 * Время последнего отправленного сообщения в группе
	 *
	 * @var int
	 */
	public $lastMessageTime;

	/**
	 * Время последнего отправленного не сервисного сообщения в группе
	 *
	 * @var
	 */
	public $lastNonServiceMessageTime;

	/**
	 * Время последней активности в группе
	 *
	 * @var int
	 */
	public $lastActivityTime;

	/**
	 * Список id профилей пользователей, которые писали сообщения в группу,
	 * упорядоченный в хронологиеском порядке добавления первого сообщения<br>
	 * <br>
	 * Пример для понимания:<br>
	 * - в группе есть пользователи id1, id2, id3<br>
	 * - хронологический список сообщений в группе:<br>
	 * <ul>
	 *        <li>id2: сообщение</li>
	 *        <li>id1: сообщение</li>
	 *        <li>id2: сообщение</li>
	 *        <li>id3: сообщение</li>
	 *        <li>id1: сообщение</li>
	 * </ul>
	 * <br>
	 * Значение данной переменной: ['id2', 'id1', 'id3']<br>
	 * <br>
	 * Добавлять значения нужно так:<br>
	 * //... получение экземпляра группы ($groupModel)<br>
	 * $groupModel->addProfileToAuthorsOrderedPool($userProfileId);<br>
	 *
	 * @var array
	 */
	public $authorsOrderedPool;

	/**
	 * Добавляет id профиля пользователя в пул авторов @see authorsOrderedPool
	 *
	 * @param $profileId - id профиля пользователя
	 */
	public function addProfileToAuthorsOrderedPool($profileId)
	{
		if (!in_array($profileId, (array)$this->authorsOrderedPool)) {
			$this->authorsOrderedPool[] = $profileId;
		}
	}

	/**
	 * Поля модели
	 *
	 * @return array
	 */
	static function fields()
	{
		return array_merge(
			parent::fields(),
			[
				'authorsOrderedPool' => self::MIXED,
				'lastMessageTime' => self::INT,
				'lastNonServiceMessageTime' => self::INT,
				'lastActivityTime' => self::INT,
				'relations' => [
					Many::className(),
					Relations::className(),
					Profile::className(),
				],
				'unseenMessages' => [
					Many::className(),
					Unseen::className(),
					Profile::className(),
				],
			]
		);
	}

	/**
	 * Возвращает список групп, в которых присутствуют пользователи с перечисленными profileId
	 *
	 * @param string[] $profileIds - массив с id профилей
	 * @param int $limit - лимит выбираемых записей
	 * @return array|null
	 */
	public static function getList(array $profileIds, $limit = null)
	{
		$finder = self::find();

		if ($limit !== null) {
			$finder->limit($limit);
		}

		$finder->orderReverse('lastActivityTime');
		$finder->where('relations._ref', $profileIds, Finder::OP_ALL);

		return $finder->all();
	}

	/**
	 * Возвращает список групп, к которым пользователь имеет определенное отношение
	 *
	 * @param $profileId -  id профиля пользователя
	 * @param $status - статус отношения
	 * @param int|null $limit - ограничение на количество выбираемых групп
	 * @return Groups[]
	 */
	public function getListWithProfileStatus($profileId, $status, $limit = null)
	{
		/** @var Groups[] $allGroups */
		$allGroups = $this->getList([$profileId], $limit);

		$groups = [];
		foreach ($allGroups as $group) {
			/** @var Relations $relation */
			foreach ($group->relations as $relation) {
				if ($relation->status == $status && $relation->_ref == $profileId) {
					$groups[] = $group;
				}
			}
		}

		return $groups;
	}

	/**
	 * Возвращает true, если значение отношения профиля $targetProfileId к данной группе совпадает со $status
	 *
	 * @param $targetProfileId string - id профиля
	 * @param $status int - статус отношения
	 * @return bool
	 * @throws \Exception
	 */
	public function hasRelation($targetProfileId, $status)
	{
		if (empty($targetProfileId)) {
			throw new \Exception('Пустой Id группы', 404);
		}

		if (isset($this->relations[$targetProfileId])) {
			return $this->relations[$targetProfileId]->status == $status;
		}

		throw new \Exception('Указанного пользователя нет в группе.', 404);
	}

	/**
	 * Возвращает статус отношения к данной группе пользователя с профайлом $targetProfileId
	 *
	 * @param string $targetProfileId - id профиля пользователя
	 * @return int
	 * @throws \Exception
	 */
	public function getRelationStatus($targetProfileId)
	{
		if (!isset($this->relations[$targetProfileId])) {
			throw new \Exception('Указанного пользователя нет в группе.', 404);
		}

		return (int)$this->relations[$targetProfileId]->status;
	}

	/**
	 * Устанавливает статус отношения к данной группе пользователя с профайлом $targetProfileId
	 *
	 * @param string $targetProfileId - id профиля пользователя
	 * @param int $newStatus - новый статус отношения к группе
	 * @throws \Exception
	 */
	public function setRelationStatus($targetProfileId, $newStatus)
	{
		/** @var Relations $relation */
		foreach ($this->relations as $relation) {
			if ($relation == $targetProfileId) {
				$relation->status = (int)$newStatus;
				$this->save();
				return;
				break;
			}
		}

		throw new \Exception('Указанного пользователя нет в группе.', 404);
	}


	/**
	 * Начинает диалог с [новым] пользователем
	 *
	 * @param string $newProfileId
	 * @param string $myProfileId
	 * @return static
	 */
	static function newDialog($newProfileId, $myProfileId)
	{
		if (empty($myProfileId) || empty($newProfileId)) {
			throw new \Exception('Wrong arguments.');
		}

		$myProfile = Profile::find($myProfileId);

		if ($myProfile == null) {
			throw new \Exception("Profile '$myProfile' not found.");
		}

		$newProfile = Profile::find($newProfileId);

		if ($newProfile == null) {
			throw new \Exception("Profile '$newProfile' not found.");
		}

		# Пытаемся получить группу, в которой есть оба пользователя
		$neededGroup = static::getList([$newProfileId, $myProfileId], 1)[0];
		# Если такой группы нет (пользователи не пытались общаться друг с другом) - создаем группу
		if (!$neededGroup) {
			$neededGroup = new static();
			# Статус отношения к данной группе потенциального получателя сообщений - неизвестный
			$neededGroup->relations[$newProfileId]->status = Relations::STATUS_UNKNOWN;
			# Статус отношения к данной группе потенциального отправителя сообщений - нейтральный
			$neededGroup->relations[$myProfileId]->status = Relations::STATUS_NEUTRAL;
			$neededGroup->save();
		} elseif ($neededGroup->relations[$myProfileId]->status == Relations::STATUS_UNKNOWN) {
			$neededGroup->relations[$myProfileId]->status = Relations::STATUS_NEUTRAL;
			$neededGroup->save();
		}

		return $neededGroup;
	}

	public static function getUnreadMessagesCountForProfile($targetProfileId)
	{
		if (empty($targetProfileId)) {
			throw new \Exception('Пустой Id профиля', 500);
		}

		# Получаем список групп, в которых состоит пользователь
		/** @var Groups[] $groups */
		$groups = Groups::getList([$targetProfileId]);

		# Подсчитываем непрочитанные сообщения от всех групп, кроме заблокированных
		$count = 0;
		foreach ((array)$groups as $group) {
			if (!isset ($group->relations[$targetProfileId])) {
				continue;
			}

			if ($group->relations[$targetProfileId]->status == Relations::STATUS_BLOCKED) {
				continue;
			}

			$count += $group->unseenMessages[$targetProfileId]->count;
		}

		return $count;
	}

	public static function changeRelation($initiatorProfileId, $targetProfileId, $newRelationStatus)
	{
		# Получаем экземпляр группы
		/** @var \Messenger\Groups $groupModel */
		$groupModels = Groups::getList([$initiatorProfileId, $targetProfileId], 1);
		$groupModel = null;
		if (count($groupModels) > 0) {
			$groupModel = $groupModels[0];
		} else {
			Groups::newDialog($targetProfileId, $initiatorProfileId);
			$groupModels = Groups::getList([$initiatorProfileId, $targetProfileId], 1);
			$groupModel = $groupModels[0];
		}
		# Перебираем все отношения
		/** @var Relations $relation */
		foreach ($groupModel->relations as $relation) {
			# Когда нашли свое отношение к этой группе
			if ($relation->_ref == (string)$initiatorProfileId) {

				# Меняем статус отношения и сохраняем изменения в группе
				/** @var \Messenger\Relations $relation */
				$relation->status = $newRelationStatus;
				$groupModel->save();

				return;
			}
		}

		throw new \Exception('Неверный Id группы', 500);
	}

	public static function getUnreadMessagesCountForMessengerTab($profileId, $listStatus)
	{
		$groupsModel = new Groups();
		$groups = $groupsModel->getListWithProfileStatus($profileId, $listStatus);

		$messagesCount = 0;
		foreach ($groups as $group) {
			/** @var Relations $relation */
			foreach ($group->relations as $relation) {
				if ($relation->_ref == $profileId || $relation->status == Relations::STATUS_BLOCKED) {
					continue;
				}

				if (isset($group->unseenMessages[$profileId])) {
					$messagesCount += $group->unseenMessages[$profileId]->count;
				}
			}
		}

		return $messagesCount;
	}

	/**
	 * @return Model2|Model2\Finder
	 */
	protected static function getCompanionProfileIdsWithStatusFinder($myProfileId, $status)
	{
		/** @var Groups[] $groups */
		$finder = static::find();

		$finder->where(
			'relations',
			[
				'_ref' => (string)$myProfileId,
				'status' => (int)$status,
			],
			Finder::OP_EQ
		);

		return $finder;
	}

	public static function getCompanionProfileIdsWithStatusCount($myProfileId, $status)
	{
		$finder = static::getCompanionProfileIdsWithStatusFinder($myProfileId, $status);
		return $finder->count();
	}

	public static function getCompanionProfileIdsWithStatus($myProfileId, $status, $offset = null, $limit = null)
	{
		$idList = [];
		$finder = static::getCompanionProfileIdsWithStatusFinder($myProfileId, $status);
		$finder->orderReverse('lastActivityTime');

		if ($offset) {
			$finder->offset($offset);
		}

		if ($limit) {
			$finder->limit($limit);
		}

		$groups = $finder->all();

		foreach ($groups as $group) {
			foreach ($group->relations as $relation) {
				if ($relation->_ref == $myProfileId) {
					continue;
				}

				$idList[] = (string)$relation;
			}
		}

		return $idList;
	}

	public function beforeSave()
	{
		if ($this->lastActivityTime !== null) {
			$this->lastActivityTime = intval(microtime(true) * 10000.0);
		}

		$this->lastActivityTime = $this->lastMessageTime;
	}

	public function getUnreadMessages(array $authorIds) {
		$finder = Messages::find();
		$finder->where('group._ref', (string)$this);
		$finder->where('author._ref', $authorIds, Finder::OP_IN);
		$finder->orderReverse('sendTimestamp');
		return $finder->all();
	}
}
